iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0
自我挑戰組

Go in 3o系列 第 14

[Day14] Go in 30 - 錯誤處理 -認識標準函式庫error騷兩圈

  • 分享至 

  • xImage
  •  

一、本篇提要

本篇主要檢視Go語言標準套件本身的程式碼片段,來了解Go錯誤處理的慣例,以及如何建立自訂錯誤error。

二、瞧瞧 Go 的錯誤處理

大部分程式語言(如筆者我Java仔),如果某些功能有發生錯誤的可能性,我們就得寫try....catch去包住,但在Go中很明確的會先接收到error值,然後自己判斷該對他做些啥事。

val err := someFunc() //呼叫函式,接收回傳值(包括error)
if err != nil {
	//若有錯誤存在,做些處理然後把它繼續往外傳
	return err
}
return nil //沒有錯誤,對上一層回nil

如果只是要檢查一個函式是否正確執行,可以寫如下 簡潔一點:

if _,err := someFunc(); err != nil {

}

Go 語言藉由這種方式,明白要求開發者,承擔起處理錯誤的責任,不僅簡化了檢查錯誤的流程,也讓開發者付出更多心力在關注程式碼的潛在問題。

接下來我們來瞧瞧Go語言中的錯誤值到底是啥。

三、error 介面

3.1 Go 語言中的error值

在 Go 語言中,一個error是一個值。
Go語言創始人 Rob Pike 對 error 是這樣形容 :

資料值是能用程式設定的,而既然error也是值,所以,error 同樣可以用程式設定內容。
error 不是程式例外,他並無任何特別之處,因為未處理的例外會讓程式當掉,但error卻不見得。

既然error是一個值,那麼就可以把它當作引數傳給函式、被函式傳回,並能像Go語言中任何值依樣被取出來做比較。

Go 語言的error值都必須實作來符合error介面的定義(介面實作會在後續篇幅說明),以下是Go的error介面 :

type error interface(){
	Error() string
}

**Note : 這個型別被宣告在最高層級,任何Go程式都可以直接取用,一個型別只要符合介面的規範,就會被視作為符合該介面型別,也就是 : 任何型別只要符合error介面的要求,就能當作error型別: **

* 型別得要擁有一個函式(或方法)叫做 Error()

  • Error() 得回傳一個string型別的值

由以下程式內的錯誤處理,來說明Go如何處理錯誤 :

package main

import (
    "fmt"
    "strconv"
)

func main() {

    v := "10"
    s, err := strconv.Atoi(v)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("%T, %v\n", s, s)

    v = "s2"
    s2, err := strconv.Atoi(v)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("%T, %v\n", s2, s)
}

從上面看到,Go標準函式庫strconv.Atoi()會接收一個字串,並遵循Go語言的設計慣例回傳一個int,以及一個error值。任何函式若要回傳錯誤,error應該要當作最後一個傳回值。

簡單的複習下,可以注意到main()中err變數是重複使用的,我們之前有提到短變數宣告,只要左側其中只少有一新變數既可成立,範例中 (s, err)、(s2, err)。

若函釋回傳error 還不理它,是很糟糕 ^ ^ 的習慣,之後debug痛苦na。

error 值是nil代表沒有錯誤,但若不是nil就是有了,因應場合不同,我們可以 :

  • 將error丟給函式呼叫者
  • 用 log 記錄下來,繼續執行
  • 停止程式執行
  • 忽略它 ^ ^
  • 引發 panic (只有罕見狀況會這樣做,稍後提)

3.2 error 型別定義

來研究Go語言標準套件的error型別,我們先從error.go開始

// https://golang.org/src/errors/errors.go
type errorString struct {
	s string
}

errorString 這個結構位於 error 套件中,裡面有一個字串型別的欄位 s,來儲存錯誤內容。注意到 errorString 型別和其欄位 s 都是以小寫開頭,代表他們不是公開的,也就代表我們不能在外部程式直接使用它。


package main

import (
	"errors"
	"fmt"
)

func main() {
	es := errors.errorString{}
	es.s = "slacker"
	fmt.Println(es)
}

https://ithelp.ithome.com.tw/upload/images/20230929/20162693rO5NCKC6cN.png

但是呢,error.go中還有這麼一段 :

// https://golang.org/src/errors/errors.go
type errorString struct {
	s string
}

//這一段
func (e *errorString) Error() string {
	return e.s
}

Error()函式,透過指標接收器變成errorString結構的方法,這樣一來errorString結構定義就符合error介面要求,使errorString結構可以被視為error型別。

而如果你需要查看錯誤內容,只需要呼叫Error()方法和取得傳回字串即可,這意味著你可以用各式各樣方法定義error值,而它的型別只要符合error介面,你就能用完全一樣的方法操作。

接下來我們會在下一篇來建立自訂error值~


上一篇
[Day13] Go in 30 - 函式 - 以函式為型別的參數
下一篇
[Day15] Go in 30 - 錯誤處理 panic
系列文
Go in 3o30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言